package com.zzyl.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.aliyun.iot20180120.Client;
import com.aliyun.iot20180120.models.*;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.zzyl.base.PageResponse;
import com.zzyl.constant.CacheConstants;
import com.zzyl.dto.DeviceDto;
import com.zzyl.dto.DevicePageQueryDto;
import com.zzyl.entity.Device;
import com.zzyl.enums.BasicEnum;
import com.zzyl.enums.DeviceLocationTypeEnum;
import com.zzyl.enums.DevicePhysicalLocationTypeEnum;
import com.zzyl.exception.BaseException;
import com.zzyl.mapper.DeviceMapper;
import com.zzyl.properties.AliIoTConfigProperties;
import com.zzyl.service.DeviceService;
import com.zzyl.utils.ObjectUtil;
import com.zzyl.vo.DeviceVo;
import com.zzyl.vo.ProductVo;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * Description DeviceServiceImpl
 *
 * @author zhangyan
 * @time 2024/10/30
 */

@Service
@Slf4j
@RequiredArgsConstructor
public class DeviceServiceImpl implements DeviceService {


    private final Client client;
    private final AliIoTConfigProperties aliIoTConfigProperties;
    private final StringRedisTemplate redisTemplate;

    @Override
    public void syncProductList() {

        //调用查询IOT所有产品列表：QueryProductList
        //1.构建查询产品列表请求对象
        QueryProductListRequest request = new QueryProductListRequest();
        request.setPageSize(200);
        request.setCurrentPage(1);
        request.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        //2.client执行查询产品列表接口调用，返回响应对象
        QueryProductListResponse response = null;
        try {
            response = client.queryProductList(request);
        } catch (Exception e) {
            //3.处理远程接口调用异常，结束应用程序
            e.printStackTrace();
            throw new RuntimeException(e);
        }


        //4.获取响应体success判断是否调用成功，失败抛出异常，结束程序
        if (!response.getBody().getSuccess()) {
            log.error("调用IOT接口失败，响应信息：{}", response.getBody().getErrorMessage());
            throw new BaseException(BasicEnum.IOT_LIST_PRODUCT_ERROR);
        }
        //5.获取响应体 body.data.list.productInfo集合
        List<QueryProductListResponseBody.QueryProductListResponseBodyDataListProductInfo> productInfoList
                = response.getBody().getData().getList().getProductInfo();
        //6.将productInfo集合转换为List<ProductVo>
        List<ProductVo> productVoList = BeanUtil.copyToList(productInfoList, ProductVo.class);

        //7.写入缓存
        redisTemplate.opsForValue()
                .set(CacheConstants.ALL_PRODUCT_KEY, JSONUtil.toJsonStr(productVoList));

    }

    @Override
    public List<ProductVo> allProduct() {

        String jsonStr = redisTemplate.opsForValue().get(CacheConstants.ALL_PRODUCT_KEY);
        if (StrUtil.isEmpty(jsonStr)) {
            return JSONUtil.toList(jsonStr, ProductVo.class);
        }
        return JSONUtil.toList(jsonStr, ProductVo.class);
    }

    private final DeviceMapper deviceMapper;

    @Override
    public void registerDevice(DeviceDto deviceDto) {
        //判断设备是否存在
        Long count = deviceMapper.countByDeviceName(deviceDto.getDeviceName());
        if (count > 0) {
            //抛出异常注册失败
            log.error("注册失败,设备已存在");
            throw new BaseException(BasicEnum.IOT_REGISTER_DEVICE_ERROR);
        }
        //2.远程调用IOT注册设备RegisterDevice接口，并且获取生成的设备id
        //从dto中获取设备注册请求参数
        RegisterDeviceRequest request = deviceDto.getRegisterDeviceRequest();
        //注册设备请求对象封装IotInstanceId属性
        request.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        //远程调用注册设备接口获取返回RegisterDeviceResponse对象
        RegisterDeviceResponse response = null;
        try {
            response = client.registerDevice(request);
            //捕获远程调用可能发生的异常,抛出友好提示异常
        } catch (Exception e) {
            log.error("注册设备失败,远程调用异常");
            throw new BaseException(BasicEnum.IOT_REGISTER_DEVICE_ERROR);
        }
        if (!response.getBody().getSuccess()) {
            //获取RegisterDeviceResponse对象返回数据success判断是否调用成功，失败抛出友好提示异常
            log.error("注册设备失败,响应信息：{}", response.getBody().getErrorMessage());
            throw new BaseException(BasicEnum.IOT_REGISTER_DEVICE_ERROR);
        }
        //接口调用成功，获取生成的设备id（响应体data.IotId），封装到deviceDto
        deviceDto.setIotId(response.getBody().getData().getIotId());
        //3.deviceDto转换为device对象
        Device device = BeanUtil.toBean(deviceDto, Device.class);
        //4.补全产品的名字，远程调用查询产品QueryProduct接口
        //构建查询产品请求对象，封装属性ProductKey和IotInstanceId
        QueryProductRequest request1 = new QueryProductRequest();
        request1.setProductKey(device.getProductKey());
        request1.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        //远程调用查询设备接口获取返回QueryProductResponse对象
        QueryProductResponse queryProductResponse = null;

        try {
            queryProductResponse = client.queryProduct(request1);
        } catch (Exception e) {
            //捕获远程调用可能发生的异常,抛出友好提示异常
            log.error("注册设备失败,远程调用查询异常");
            throw new BaseException(BasicEnum.IOT_QUERY_PRODUCT_ERROR);
        }
        if (!queryProductResponse.getBody().getSuccess()) {
            //获取QueryProductResponse对象返回数据success判断是否调用成功，失败抛出友好提示异常
            log.error("注册设备失败,响应信息：{}", queryProductResponse.getBody().getErrorMessage());
            throw new BaseException(BasicEnum.IOT_QUERY_PRODUCT_ERROR);
        }

        //接口调用成功，获取产品名字（响应体data.ProductName），封装到device
        device.setProductName(queryProductResponse.getBody().getData().getProductName());
        //5.判断设备位置绑定类型是否为随身设备，如果是设置物理班绑定类型（physicalLocationType）为-1
        if (DevicePhysicalLocationTypeEnum.ELDER.getOrdinal().equals(device.getPhysicalLocationType())) {
            device.setPhysicalLocationType(-1);
        }
        //6.将设备保存到数据库
        //补全是否包含门禁，默认设置为0
        device.setHaveEntranceGuard(0);
        //调用mapper保存
        int i = deviceMapper.insertSelective(device);
        if (i == 0) {
            //捕获异常，远程调用删除IOT设备接口（DeleteDevice）
            //构建查删除IOT设备请求对象，封装属性IotInstanceId和IotId
            DeleteDeviceRequest deleteDeviceRequest = new DeleteDeviceRequest();
            deleteDeviceRequest.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
            deleteDeviceRequest.setIotId(device.getIotId());
            deleteDeviceToAli(deleteDeviceRequest);
            log.error("注册设备失败原因:{}", "数据库添加失败");
            throw new BaseException(BasicEnum.IOT_REGISTER_DEVICE_ERROR);
        }


    }

    /**
     * 调用远程删除设备
     *
     * @param deleteDeviceRequest
     */
    private void deleteDeviceToAli(DeleteDeviceRequest deleteDeviceRequest) {

        DeleteDeviceResponse deleteDeviceResponse;
        //远程调用删除设备接口
        try {
            deleteDeviceResponse = client.deleteDevice(deleteDeviceRequest);
        } catch (Exception e) {
            //捕获删除设备远程接口调用发生异常（如果其企业中删除失败，会记录日志，通知管理员（邮件或手机短信））
            log.error("删除设备失败,远程调用异常");
            throw new BaseException(BasicEnum.IOT_DELETE_DEVICE_ERROR);
        }
        if (!deleteDeviceResponse.getBody().getSuccess()) {
            //获取QDeleteDeviceResponse对象返回数据success判断是否调用成功，失败抛出友好提示异常（如果其企业中删除失败，会记录日志，通知管理员（邮件或手机短信））
            //抛出异常结束，提示友好信息
            log.error("删除设备失败,响应信息：{}", deleteDeviceResponse.getBody().getErrorMessage());
            throw new BaseException(BasicEnum.IOT_DELETE_DEVICE_ERROR);
        }

    }

    /**
     * 设备分页查询
     *
     * @param devicePageQueryDto
     * @return
     */
    @Override
    public PageResponse<DeviceVo> pageQueryDevice(DevicePageQueryDto devicePageQueryDto) {
        PageHelper.startPage(devicePageQueryDto.getPageNum(), devicePageQueryDto.getPageSize());
        Page<DeviceVo> page = deviceMapper.findDeviceListByCondition(devicePageQueryDto);

        return PageResponse.of(page, DeviceVo.class);
    }

    /**
     * 查询设备详细信息
     *
     * @param request
     * @return
     */
    @Override
    public DeviceVo queryDeviceDetail(QueryDeviceDetailRequest request) {
        //从数据库中查询
        DeviceVo deviceVo = deviceMapper.queryDeviceDetailByIotId(request.getIotId());
        if (deviceVo == null) {
            log.error("该设备不存在");
            throw new RuntimeException("该设备不存在");
        }
        //构建远程调用查询设备详细信息接口请求对象(补全所需要的属性)
        request.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        //定义返回值
        QueryDeviceDetailResponse response=null;
        try {
            response = client.queryDeviceDetail(request);
        } catch (Exception e) {
            log.error("查询设备详细信息失败,远程调用异常:{}",e.getMessage());
        }
        if (response.getBody().getSuccess()){
            //封装返回值
            BeanUtil.copyProperties(response.getBody().getData(),deviceVo);
        }else {
            log.error("查询设备详细信息失败,响应信息：{}",response.getBody().getErrorMessage());
        }


        return deviceVo;
    }

    /**
     * 删除设备
     *
     * @param deleteDeviceRequest
     */
    @Override
    public void deleteDevice(DeleteDeviceRequest deleteDeviceRequest) {
        int i = deviceMapper.deleteByIotId(deleteDeviceRequest.getIotId());
        if (i == 0){
            throw new RuntimeException("删除设备失败");
        }
        deleteDeviceRequest.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        deleteDeviceToAli(deleteDeviceRequest);
    }

    /**
     * 查询设备属性状态
     *
     * @param request
     * @return
     */
    @Override
    public QueryDevicePropertyStatusResponse queryDevicePropertyStatus(QueryDevicePropertyStatusRequest request) {

        request.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        QueryDevicePropertyStatusResponse response = null;
        try {
            response = client.queryDevicePropertyStatus(request);
        } catch (Exception e) {
            log.error("查询设备属性状态失败,远程调用异常:{}",e.getMessage());
            throw  new  BaseException(BasicEnum.IOT_QUERY_DEVICE_PROPERTY_STATUS_ERROR);
        }
        if (!response.getBody().getSuccess()){
            log.error("查询设备属性状态失败:{}",response.getBody().getErrorMessage());
        }
        return response;
    }

    /**
     * 查看制定产品的已发布的物模型功能定义详情
     *
     * @param request
     * @return
     */
    @Override
    public QueryThingModelPublishedResponse queryThingModelPublished(QueryThingModelPublishedRequest request) {

        request.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        QueryThingModelPublishedResponse response = null;
        try {
            response = client.queryThingModelPublished(request);
        } catch (Exception e) {
            log.error("查看制定产品的已发布的物模型功能定义详情失败,远程调用异常:{}",e.getMessage());
            throw  new  BaseException(BasicEnum.IOT_QUERY_THING_MODEL_PUBLISHED_ERROR);
        }
        if (!response.getBody().getSuccess()){
            log.error("查看制定产品的已发布的物模型功能定义详情失败:{}",response.getBody().getErrorMessage());
        }
        return response;
    }

    /**
     * 修改设备
     *
     * @param deviceDto
     */
    @Override
    public void updateDevice(DeviceDto deviceDto) {
        //检验数据库是否存在设备
        Device dbDevice = deviceMapper.selectByPrimaryKey(deviceDto.getId());
        if (ObjectUtil.isEmpty(dbDevice)) {
            throw new BaseException(BasicEnum.DEVICE_NOT_EXIST);
        }
        BatchUpdateDeviceNicknameRequest request = new BatchUpdateDeviceNicknameRequest();
        BatchUpdateDeviceNicknameRequest.BatchUpdateDeviceNicknameRequestDeviceNicknameInfo batchUpdateDeviceNicknameRequestDeviceNicknameInfo = BeanUtil.copyProperties(deviceDto, BatchUpdateDeviceNicknameRequest.BatchUpdateDeviceNicknameRequestDeviceNicknameInfo.class);
        request.setDeviceNicknameInfo(ListUtil.toList(batchUpdateDeviceNicknameRequestDeviceNicknameInfo));
        request.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        //修改远程数据
        BatchUpdateDeviceNicknameResponse response=null;
        try {
           response = client.batchUpdateDeviceNickname(request);
        } catch (Exception e) {
            log.error("修改设备失败,远程调用异常:{}",e.getMessage());
            throw new BaseException(BasicEnum.IOT_BATCH_UPDATE_DEVICE_ERROR);
        }
        if (!response.getBody().getSuccess()){
            log.error("修改设备失败,响应信息：{}",response.getBody().getErrorMessage());
        }
        //修改数据库数据
        Device device = BeanUtil.toBean(deviceDto, Device.class);
        //如果是随身设备，物理位置设为-1
        if (device.getLocationType().equals(DeviceLocationTypeEnum.PORTABLE_DEVICE.getOrdinal())) {
            device.setPhysicalLocationType(DevicePhysicalLocationTypeEnum.ELDER.getOrdinal());
        }
        int i = deviceMapper.updateByPrimaryKeySelective(device);
        if (i == 0){
            throw new RuntimeException("修改设备失败");
        }

    }
}
